// 引入pinia的 defineStore 函数 创建store
import { defineStore } from 'pinia'
// 引入api请求函数
import userApi from '@/api/userInfo'
// 导入在api中定义的登录接口参数类型
// 注意：导入接口参数类型的时候一定要在 import后面加上 type
import type {LoginParamsData,UserInfoData} from '@/api/userInfo'
// 引入定义的三大路由器（静态路由、动态路由、任意路由）
import {staticRoutes,allAsyncRoutes,anyRoute} from '@/router/routes'
// 路由自带的数据类型
import type { RouteRecordRaw } from 'vue-router'
// 引入路由
import router from '@/router'
// 引入深拷贝
import { cloneDeep } from 'lodash';


// pinia的state是一个函数，函数的形参和返回值类型要确定
interface UserInfoStoreStateData {
  token:string | null,
  userInfo:UserInfoData,
  menuRoutes:RouteRecordRaw[]
}

// 1. 这个函数在根据用户返回的菜单权限信息数据 result.routes（name字符串数组）
// 从所有的动态路由当中过滤出自己的动态路由
function filterAsyncRoutes(allAsyncRoutes:RouteRecordRaw[],routeNames:string[]){
  let res = allAsyncRoutes.filter(item => {
    // 如果name字符串数组当中可以找到 allAsyncRoutes当中存在的name
    if(routeNames.indexOf(item.name as string) !== -1) {
      // 这里还需要过滤二级路由(如果路由存在children并且children长度不为0)
      if(item.children && item.children.length){
        // 过滤二级路由，过滤出来之后把原来的给覆盖掉，否则二级路由是没办法权限控制的
        // 把我们配置的总的动态路由相关的二级路由给彻底覆盖掉了
        // 后期其他用户再去登录过滤的时候，这个二级路由就不是总的二级路由，就变为上一次用户过滤后的二级路由
        item.children = filterAsyncRoutes(item.children,routeNames)
      }
      return true
    }
  })
  return res
}

// 2. 封装把多个路由进行动态添加注册的函数
function addRoutes(routes:RouteRecordRaw[]){
  // 遍历每一项路由
  routes.forEach(item => {
    // 调用路由自带的.addRoute()动态添加注册路由
    router.addRoute(item)
  })
}

// 封装用户退出后重置路由信息
function resetRouter() {
  // 1.获取到路由器当中所有的路由
  // .getRoutes() 路由器自带获取所有路由的方法
  let routes = router.getRoutes()

  // 2. 遍历所有的路由，一个一个删除
  routes.forEach(item => {
    // .removeRoute() 路由器自带删除路由方法
    router.removeRoute(item.name as string)
  })

  // 3. 调用封装的addRoutes()函数，把staticroutes再添加一遍
  addRoutes(staticRoutes)
}

export const useUserInfoStore = defineStore({
  id:'userInfo',
  state():UserInfoStoreStateData{
    return {
      // token:'', 如果你只是为了登录，那么就直接初始化为空串
      // 如果你为了后期能够自动登录（免登录），此时就要把token保存在本地
      // 初始化的时候要先从本地去拿,如果能拿到就拿，拿不到就是个'' 空串
      token:localStorage.getItem('token_key') || '',
      userInfo:{
        // 初始化数据
        "routes": [], //菜单权限信息数据，一堆字符串,对应的是路由name值，需要根据这些字符串从所有的异步路由当中去过滤自己的异步路由
        "buttons": [],
        "roles": [],
        "name": '',
        "avatar": ''
      },
      menuRoutes:staticRoutes
    }
  },
  actions:{
    // 先写登录的逻辑
    /* ***********************************************************
      // async都是添加在函数定义前面，async本意就是异步的意思，
      // 添加了async的函数叫异步函数，
      // 异步函数内部一般情况下都有异步操作，但是不绝对
      
      // async函数特点:功能 参数  返回值，
      // async函数返回值一定 是promise,不看return，

      // 函数返回的promise是成功还是失败看return，
      // return的结果决定函数返回的promise是成功还是失败
      分以下情况：return是promise还是非promise
          非promise数据
            正常值都是成功的，成功的结果就是return的结果
            抛出异常就是失败的，失败的原因是抛出的异常
          promise数据
            如果return的promise是成功的，那么函数返回的promise就是成功的，
            成功的结果就是return的promise成功的结果.
            如果return的promise是失败的，那么函数返回的promise就是失败的，
            失败的原因就是return的promise失败的原因.
    ************************************************************ */
    async login(loginParams:LoginParamsData){
      try {
        // 携带参数 发送请求
        const result = await userApi.userLogin(loginParams)
        // 把获取到的数据赋值给state的token
        this.token = result.token
        // 把获取到的token存到本地
        localStorage.setItem('token_key',result.token)
        // 组件当中发请求如何知道请求是成功还是失败，进而决定不同的逻辑
        return 'ok'
      } catch (error) {
        // 请求失败，继续传递失败的Promise
        return Promise.reject(error)
      }
    },

    // 获取用户信息逻辑
    async getUserInfo(){
      try {
        // 调用获取用户信息 API
        const result = await userApi.getUserInfo()
        // 把获取到的数据赋值给state的userInfo
        this.userInfo = result

        /* 菜单权限控制执行流程 */
        // 1. 根据routes和allAsyncRoutes过滤当前这个用户的动态路由，封装函数
        // 注意：每次深拷贝一份所有的路由给他，就不会出现子路由覆盖的问题
        let userAsyncRoutes = filterAsyncRoutes(cloneDeep(allAsyncRoutes) ,result.routes)

        // 2. 动态注册添加到路由器
        // 路由器给我们提供了api  vue2当中提供了api router.addRoutes(数组) 可以一次性把多个路由动态添加注册到路由器
        // vue3路由当中只提供了router.addRoute,它每次只能动态添加一个
        // 把自己的动态路由和任意路由，动态添加注册到路由器,目的是后期点击可以跳转，
        // 但是此时菜单并没有产生
        addRoutes(userAsyncRoutes.concat(anyRoute))

        // 3. 把需要遍历的路由数据改为当前路由器中注册的路由数据
        this.menuRoutes = staticRoutes.concat(userAsyncRoutes,anyRoute)

        return 'ok'
      } catch (error) {
        // 请求失败，继续传递失败的Promise
        return Promise.reject(error)
      }
    },

    // 重置用户信息和token逻辑
    async reset(){
        // 清空用户信息
        this.token = ''
        /*
        //  写法一：开销比较大(改变了对象的地址)
        this.userInfo = {
          "routes": [],
          "buttons": [],
          "roles": [],
          "name": '',
          "avatar": ''
        } */
        /* 写法二：开销小(不改变对象的地址) */
        Object.assign(this.userInfo,{
          "routes": [],
          "buttons": [],
          "roles": [],
          "name": '',
          "avatar": ''
        })
        // 清除用户的token
        localStorage.removeItem('token_key')
    },

    // 退出登录逻辑
    async logout(){
      try {
        // 调用退出登录 API
        await userApi.ueerLogout()
        // 清空用户信息和token
        this.reset()
        // 调用重置路由函数
        // 最终它是要写上，重置路由和不重置路由不会造成菜单的不同，但是不用的话会影响路由跳转
        resetRouter()
        return 'ok'
      } catch (error) {
        // 请求失败，继续传递失败的Promise
        return Promise.reject(error)
      }
    }
  },
  getters:{

  }
})